package com.zzyl.nursing.job;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.service.IAlertRuleService;
import com.zzyl.nursing.service.IDeviceDataService;
import com.zzyl.nursing.service.IDeviceService;
import com.zzyl.nursing.ws.WebSocketServer;
import com.zzyl.system.service.ISysRoleService;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 报警数据的处理任务类
 */
@Component
@Slf4j
public class AlertDataJob {
    @Autowired
    private IAlertRuleService alertRuleService;
    @Autowired
    private IDeviceService deviceService;
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;
    @Autowired
    private IAlertDataService alertDataService;
    @Autowired
    private ISysRoleService sysRoleService;
    @Autowired
    private WebSocketServer webSocketServer;
    public void processAlertData(){
        log.info("第一步，查询所以报警规则");
        LambdaQueryWrapper<AlertRule> qw = new LambdaQueryWrapper<>();
        //1:启用 0:禁用
        qw.eq(AlertRule::getStatus,1);
        List<AlertRule> list = alertRuleService.list(qw);
        if (CollUtil.isEmpty( list)){
            return;
        }
        log.info("第二步:遍历报警规则，根据规则查询其关联的产品设备");
        for (AlertRule rule : list) {
            log.info("报警规则：{}",rule.getAlertRuleName());
            String iotId = rule.getIotId();
            List<Device> deviceList;
            //获取产品设备
            if ("-1".equals(iotId)){
                LambdaQueryWrapper<Device> dqw = new LambdaQueryWrapper<>();
                dqw.eq(Device::getProductKey,rule.getProductKey());
                deviceList = deviceService.list(dqw);
            }else {
                //获取指定设备
                LambdaQueryWrapper<Device> dqw = new LambdaQueryWrapper<>();
                dqw.eq(Device::getIotId,iotId);
                deviceList= deviceService.list(dqw);
            }
            if (CollUtil.isEmpty(deviceList)){
                log.info("当前产品下没有设备");
                return;
            }
            for (Device device : deviceList) {
                log.info("第三步处理产品设备:{}",device.getDeviceName());
                //获取当前设备的属性数据
                String str = (String) redisTemplate.opsForHash().get(CacheConstants.IOT_DEVICE_LAST_DATA, device.getIotId());
                if (StrUtil.isNotEmpty(str)){
                    //转换成对象
                    List<DeviceData> dataList = JSONUtil.toList(str, DeviceData.class);
                    String functionId = rule.getFunctionId();
                    for (DeviceData data : dataList) {
                        if (functionId.equals(data.getFunctionId())){
                            //处理数据
                            log.info("处理数据：{}",data);
                            handelerData(data,rule);
                        }
                    }

                }
            }
        }
    }

    /**
     * 处理数据
     * @param data  数据
     * @param rule 报警规则
     */
    private void handelerData(DeviceData data, AlertRule rule) {
        //1.判断数据上报时间是否超过1分钟
        log.info("=================>1.判断数据上报时间【{}】是否超过一分钟",data.getAlarmTime());
        LocalDateTime now = LocalDateTime.now();
        if (data.getAlarmTime().isBefore(now.minusMinutes(1))) {
            log.info("数据上报时间超过1分钟，不处理");
            return;
        }
        //2.判断数据上报时间是否在报警生效时间段范围内，不在则放弃处理
        log.info("=================>2.判断数据上报时间【{}】是否在报警生效时间段内【{}】",data.getAlarmTime(),rule.getAlertEffectivePeriod());
        //00:00:00~23:59:59
        String alertEffectivePeriod = rule.getAlertEffectivePeriod();
        if (!DateUtils.isCurrentTimeInPeriod(alertEffectivePeriod)){
            log.info("数据上报时间不在报警生效时间段内，放弃处理");
            return;
        }
        //3.判断数据是否触发报警条件
        log.info("=================>3.判断数据【{}】是否触发报警条件【{}】",data.getDataValue(),rule.getOperator());
        boolean result=evaluateExpression(data.getDataValue() ,rule.getOperator(),rule.getValue().toString());
        String countkey= CacheConstants.ALERT_TRIGGER_COUNT.replace("{IotId}",data.getIotId()).replace("{FunctionId}",data.getFunctionId()).replace("{RuleId}",String.valueOf(rule.getId()));
        if (!result){
            log.info("数据没有触发报警条件，放弃处理");

            log.info("删除连续累计报警数据,{}", countkey);
            redisTemplate.delete(countkey);
            return;
        }
        //4.判断当前设备是否处于沉默周期，如果是，则放弃处理
        log.info("=================>4.判断当前设备是否处于沉默周期【{}】",rule.getAlertSilentPeriod());
        String silenkey=CacheConstants.ALERT_SILENT.replace("{IotId}",data.getIotId()).replace("{FunctionId}",data.getFunctionId()).replace("{RuleId}",String.valueOf(rule.getId()));
        //判断缓存中是否存在
        if (Boolean.TRUE.equals(redisTemplate.hasKey(silenkey))){
            log.info("当前设备处于沉默周期，放弃处理");
            return;
        }
        //5.判断连续报警次数是否到达连续周期阈值，没有则增加连续累计报警次数+1，有则触发报警
        log.info("=================>5.判断当前设备【{}】连续报警次数是否达到阈值【{}】",data.getIotId(),rule.getDuration());
        Long count = redisTemplate.opsForValue().increment(countkey);
        if (count >= rule.getDuration()){
            log.info("连续报警次数【{}】达到阈值【{}】，触发报警",count,rule.getDuration());
            //6.触发报警
            log.info("===============>6,触发报警");
            //6.1 删除redis缓存中的连续累计报警次数
            log.info("=================>6.1,删除连续累计报警数据,{}", countkey);
            redisTemplate.delete(countkey);
            //6.2 存储报警沉默周期到redis缓存中，沉默周期结束后删除
            log.info("=================>6.2,存储报警沉默周期到redis缓存中,{}", silenkey);
            redisTemplate.opsForValue().set(silenkey,data,rule.getAlertSilentPeriod(), TimeUnit.MINUTES);
            //6.3 报警数据写入数据库
            log.info("=================>6.3,存储报警数据到数据库,{}",data);
            //获取报警数据的接收人
            List<Long> userIds = getAlertReceiver(rule,data);
            if (CollUtil.isEmpty(userIds)){
                log.info("没有接收人，放弃处理");
                return;
            }
            List<AlertData> alertDataList = userIds.stream().map(new Function<Long, AlertData>() {
                @Override
                public AlertData apply(Long userId) {
                    AlertData alertData = new AlertData();
                    alertData.setIotId(data.getIotId());
                    alertData.setDeviceName(data.getDeviceName());
                    alertData.setProductKey(data.getProductKey());
                    alertData.setProductName(data.getProductName());
                    alertData.setFunctionId(data.getFunctionId());
                    alertData.setAccessLocation(data.getAccessLocation());
                    alertData.setLocationType(data.getLocationType());
                    alertData.setPhysicalLocationType(data.getPhysicalLocationType());
                    alertData.setDeviceDescription(data.getDeviceDescription());
                    alertData.setDataValue(data.getDataValue());
                    //规则信息
                    alertData.setAlertRuleId(rule.getId());
                    //TODO自己封装
                    alertData.setAlertReason("");
                    alertData.setType(rule.getAlertDataType());
                    //0 待处理，1 已处理
                    alertData.setStatus(0);
                    //TODO 接收人待处理
                    alertData.setUserId(userId);
                    //需要自己封装心率
                    rule.getFunctionName();
                    rule.getOperator();
                    rule.getValue();
                    rule.getDuration();
                    //心率连续3次>200
                    alertData.setAlertReason(rule.getFunctionName()+rule.getOperator()+rule.getValue()+rule.getDuration());
                    return alertData;
                }
            }).collect(Collectors.toList());
            alertDataService.saveBatch(alertDataList);

            //7.发送报警信息给接收人
            log.info("=================>7,发送报警信息给接收人");
            webSocketServer.sendMessageToHandler(userIds, data,rule);

            //7.2短信推送


        }

    }

    /**
     * 获取报警接收人
     * @param rule 报警规则
     * @param data 报警数据
     * @return 接收人id列表
     */
    private List<Long> getAlertReceiver(AlertRule rule, DeviceData data) {
        List<Long> userIds = new ArrayList<>();
        if (rule.getAlertDataType()==0){
            //老人异常数据,获取设备绑定的老人护工
            List<Long> nursingIds = deviceService.getNursingIdsByIotId(data.getIotId());
            userIds.addAll(nursingIds);
        }else {
            //设备异常数据
            List<Long> ids= sysRoleService.getUserIdsByRoleNameOrRoleKey("维修工");
            userIds.addAll(ids);
        }
        List<Long> superIds = sysRoleService.getUserIdsByRoleNameOrRoleKey("超级管理员");
        userIds.addAll(superIds);

        return userIds;
    }

    /**
     * 计算表达式
     * @param leftValue 左值
     * @param operator 操作符
     * @param rightValue 右值
     * @return
     */
    public boolean evaluateExpression(String leftValue, String operator, String rightValue) {
        try {
            BigDecimal left = new BigDecimal(leftValue);
            BigDecimal right = new BigDecimal(rightValue);

            switch (operator) {
                case ">":
                    return left.compareTo(right) > 0;
                case ">=":
                    return left.compareTo(right) >= 0;
                case "<":
                    return left.compareTo(right) < 0;
                case "<=":
                    return left.compareTo(right) <= 0;
                case "==":
                case "=":
                    return left.compareTo(right) == 0;
                case "!=":
                    return left.compareTo(right) != 0;
                default:
                    log.warn("不支持的操作符: {}", operator);
                    return false;
            }
        } catch (NumberFormatException e) {
            log.error("数值转换错误: leftValue={}, rightValue={}", leftValue, rightValue, e);
            return false;
        } catch (Exception e) {
            log.error("表达式计算错误: {} {} {}", leftValue, operator, rightValue, e);
            return false;
        }
    }



}
